package sessionbeans;

import java.util.Collections;
import java.util.List;
import java.util.LinkedList;
import javax.ejb.Stateless;

import entities.*;

import org.jboss.ejb3.annotation.RemoteBinding;
import javax.persistence.*;
/**
 * Session Bean implementation class FriendshipBean
 */
@Stateless
@RemoteBinding(jndiBinding="FriendshipBeanRemote")
public class FriendshipBean implements FriendshipBeanRemote {

	@PersistenceContext
	private EntityManager manager;
	private static final String friendFinder = "select f.key.user2 from FriendOf as f join f.key.user1 as r where r.userId = :user";
	private static final String findUsersFriends = "select f from FriendOf as f where f.key.user1.userId = :user";
	public static final String ALREADY_FRIENDS = "You are friends";
	public static final String REQUEST_SENT = "Waiting for his/her answer";
	public static final String PENDING_REQUEST = "Answer this user's request";
	public static final String CAN_SEND_REQUEST = "Send a friendship request";
    /**
     * Default constructor. 
     */
    public FriendshipBean() {
        // TODO Auto-generated constructor stub
    }

	@Override
	public List<PendingFriendship> getPendingFriendships(String userId) {
		Query query = manager.createQuery("select p from PendingFriendship as p where p.key.receiver.userId = :userId");
		query.setParameter("userId", userId);
		List<PendingFriendship> requests = (List<PendingFriendship>) query.getResultList();
		Query friendsQuery = manager.createQuery(findUsersFriends);
		friendsQuery.setParameter("user", userId);
		List<FriendOf> friends = (List<FriendOf>) friendsQuery.getResultList();
		List<PendingFriendship> reallyPending = getReallyPendingFriendships(requests, friends);
		return reallyPending;
	}

	private List<PendingFriendship> getReallyPendingFriendships(
			List<PendingFriendship> requests, List<FriendOf> friends) {
		List<PendingFriendship> results = new LinkedList<PendingFriendship>();
		for(PendingFriendship request : requests){
			boolean found = false;
			for(FriendOf friendship : friends){
				String user2 = friendship.getKey().getUser2().getUserId();
				String sender = request.getKey().getSender().getUserId();
				if(user2.equals(sender)){
					found = true;
					break;
				}
			}
			if(!found){
				results.add(request);
			}
		}
		return results;
	}

	@Override
	public boolean establishFriendship(String user1, String user2) {
		if(alreadyFriends(user1, user2) || alreadyFriends(user2, user1) || user1.equals(user2)){
			return false;
		}
		RegisteredUser reg1 = manager.find(RegisteredUser.class, user1);
		RegisteredUser reg2 = manager.find(RegisteredUser.class, user2);
		if(reg1 == null || reg2 == null){
			return false;
		}
		FriendOf friendship = new FriendOf(reg1, reg2);
		manager.persist(friendship);
		FriendOf inverse = new FriendOf(reg2, reg1);
		manager.persist(inverse);
		sendSuggestions(reg1, reg2);
		deletePreviousSuggestions(reg1, reg2);
		return true;
	}

	private void deletePreviousSuggestions(RegisteredUser reg1,
			RegisteredUser reg2) {
		IsSuggestedToPK key1 = new IsSuggestedToPK(reg1, reg2);
		IsSuggestedToPK key2 = new IsSuggestedToPK(reg2, reg1);
		IsSuggestedTo sugg1 = manager.find(IsSuggestedTo.class, key1);
		IsSuggestedTo sugg2 = manager.find(IsSuggestedTo.class, key2);
		if(sugg1 != null){
			manager.remove(sugg1);
		}
		if(sugg2 != null){
			manager.remove(sugg2);
		}
		
	}

	private void sendSuggestions(RegisteredUser reg1, RegisteredUser reg2) {
		// this suggests to reg1 some friends of reg2 and vice versa
		// if reg2 was suggested himself to reg1 then no suggestion is sent at all
		// otherwise at most 5 friends are taken from each user's friends and suggested to the other

		Query findRegFriends = manager.createQuery(friendFinder);
		String userId1 = reg1.getUserId();
		String userId2 = reg2.getUserId();
		
		if(alreadySuggestedTo(reg1, reg2)){
			return;
		}
		findRegFriends.setParameter("user", userId1);
		List<RegisteredUser> suggestionList = buildSuggestionList(findRegFriends.getResultList(), userId2);
		suggest(suggestionList, reg2);

		findRegFriends.setParameter("user", userId2);
		suggestionList = buildSuggestionList(findRegFriends.getResultList(), userId1);
		suggest(suggestionList, reg1);

	}

	private void suggest(List<RegisteredUser> suggestionList,
			RegisteredUser reg) {
		for(RegisteredUser user : suggestionList){
				IsSuggestedTo suggestion = new IsSuggestedTo(user, reg);
				manager.persist(suggestion);
		}
	}

	private List<RegisteredUser> buildSuggestionList(List resultList, String userId) {
		List<RegisteredUser> friends = (List<RegisteredUser>) resultList;
		RegisteredUser user = manager.find(RegisteredUser.class, userId);
		for(int i = 0; i < friends.size(); i++){
			RegisteredUser reg = friends.get(i);
			if(reg.equals(user) || alreadyFriends(userId, reg.getUserId()) || 
					alreadyFriends(reg.getUserId(), userId) || alreadySuggestedTo(reg, user)){
				friends.remove(i);
				i--;
			}
		}
		if(friends.size() <= 5){
			return friends;
		} else {
			Collections.shuffle(friends);
			return friends.subList(0, 5);
		}
	}

	private boolean alreadySuggestedTo(RegisteredUser reg, RegisteredUser user) {
		IsSuggestedToPK key = new IsSuggestedToPK(reg, user);
		return manager.find(IsSuggestedTo.class, key) != null;
	}

	@Override
	public List<RegisteredUser> getSuggestions(String userId) {
		RegisteredUser user = manager.find(RegisteredUser.class, userId);
		Query query = manager.createQuery("select r.key.user1 from IsSuggestedTo as r join r.key.user2 as u where u = :user");
		query.setParameter("user", user);
		return (List<RegisteredUser>)(query.getResultList());
	}
	
	private boolean alreadyFriends(String user1, String user2){
		RegisteredUser reg1 = manager.find(RegisteredUser.class, user1);
		RegisteredUser reg2 = manager.find(RegisteredUser.class, user2);
		Query query = manager.createQuery("select f from FriendOf as f where f.key.user1 = :user1 and f.key.user2 = :user2");
		query.setParameter("user1", reg1);
		query.setParameter("user2", reg2);
		return query.getResultList().size() > 0;
	}

	@Override
	public boolean deleteFriendshipRequest(String receiver, String sender) {
		if(receiver == null || sender == null){
			return false;
		}
		RegisteredUser senderUser = manager.find(RegisteredUser.class, sender);
		RegisteredUser receiverUser = manager.find(RegisteredUser.class, receiver);
		if(senderUser == null || receiverUser == null){
			return false;
		}
		PendingFriendshipPK key = new PendingFriendshipPK(senderUser, receiverUser);
		PendingFriendship request = manager.find(PendingFriendship.class, key);
		if(request == null){
			return false;
		}
		manager.remove(request);
		return true;
	}

	@Override
	public String sendFriendshipRequest(String senderId, String receiverId) {
		String ok = "Friendship request successfully sent";
		String error = "Failed to send friendship request";
		RegisteredUser sender,receiver;
		
		sender = manager.find(RegisteredUser.class, senderId);
		receiver = manager.find(RegisteredUser.class,receiverId);
		
		if ((sender != null) && (receiver != null)) {
			PendingFriendship newFriendshipRequest = new PendingFriendship(sender,receiver);
			manager.persist(newFriendshipRequest);
			return ok;
		}
		return error;
	}

	@Override
	public String getAlreadyFriendsOrAsked(String userId1, String userId2) {
		RegisteredUser user1 = manager.find(RegisteredUser.class, userId1);
		RegisteredUser user2 = manager.find(RegisteredUser.class, userId2);
		if(areAlreadyFriends(user1, user2)){
			return ALREADY_FRIENDS;
		}
		if(sentRequest(user1, user2)){
			return REQUEST_SENT;
		}
		if(sentRequest(user2, user1)){
			return PENDING_REQUEST;
		}
		return CAN_SEND_REQUEST;
		
	}
	
	private boolean areAlreadyFriends(RegisteredUser user1, RegisteredUser user2){
		FriendOfPK key1 = new FriendOfPK(user1, user2);
		FriendOf friendship1 = manager.find(FriendOf.class, key1);
		FriendOfPK key2 = new FriendOfPK(user2, user1);
		FriendOf friendship2 = manager.find(FriendOf.class, key2);
		return friendship1 != null || friendship2 != null;
	}
	
	private boolean sentRequest(RegisteredUser user1, RegisteredUser user2){
		PendingFriendshipPK key = new PendingFriendshipPK(user1, user2);
		PendingFriendship request = manager.find(PendingFriendship.class, key);
		return request != null;
	}

}
